<script setup lang="ts">
import RoomFooter from './components/RoomFooter.vue'
import RoomHeader from './components/RoomHeader.vue'
import RoomMessage from './components/RoomMessage.vue'
import type { Socket } from 'socket.io-client'
import { io } from 'socket.io-client'
import { nextTick, onMounted, onUnmounted, provide, ref } from 'vue'
import { baseURL } from '@/utils/request'
import { useConsultStore, useUserStore } from '@/stores'
import { useRoute } from 'vue-router'
import type { Message, TimeMessage } from '@/types/room'
import { MsgType, OrderStatus } from '@/enum'
import { getOrderDetail } from '@/services/consult'
import type { ConsultOrderItem, Picture } from '@/types/consult'
import dayjs from 'dayjs'
import { showToast } from 'vant'
import router from '@/router'
const store = useUserStore()
const route = useRoute()
// 聊天室的相关步骤 需要在组件挂载完成时进行
// 1. 建立连接，初始化io实例，不传默认当前服务器域名，开发过程需要传入服务器域名
// 2. 连接成功后获取默认数据
// 3. 组件移除后需要断开连接
// 接收的聊天记录数组
const chatMsgList = ref<Message[]>([])
let socket: Socket
// 将当前路由上的参数保存到Pinia中
const consult = useConsultStore()
consult.updateRoomConfig(route.query)
onMounted(() => {
  // console.log('组件创建room')

  // 在初始化时传递当前开发的后端服务器地址，并将当前用户token和订单号传过去
  // 传token，作为登录认证，传orderid作为参数（作为房间信息，区分不同的聊天室roomid）接口要求
  socket = io(baseURL, {
    // 身份认证
    auth: {
      token: `Bearer ${store.user?.token}`
    },
    query: {
      orderId: route.query.orderId
    }
  })
  // 连接成功
  socket.on('connect', () => {
    console.log('connect')
  })
  // 连接发生错误
  socket.on('error', (err) => {
    console.log('error', err)
  })
  // 断开连接
  socket.on('disconnect', () => {
    console.log('disconnect')
    chatMsgList.value = []
  })
  // 获取聊天记录
  socket.on('chatMsgList', ({ data }: { data: TimeMessage[] }) => {
    // 给每一项的items添加一项创建订单时间的数据，保证每一个分组信息中都包含有时间
    // 转换常规消息列表
    const arr: Message[] = []
    data.forEach((item, index) => {
      // 记录当下最早的时间，第一条数据的创建日期是最早的
      if (index === 0) {
        lastTime.value = item.createTime
      }
      // 将创建订单时间也加到常规消息列表中
      arr.push({
        msgType: MsgType.Notify,
        msg: {
          content: item.createTime
        },
        createTime: item.createTime,
        id: item.createTime,
        imageFlag: false
      })
      item.items.forEach((obj) => {
        obj.imageFlag = false
      })
      // 时间信息数据片段分组
      arr.push(...item.items)
    })
    chatMsgList.value.unshift(...arr)
    // 刷新成功之后需要手动关闭loading
    loading.value = false
    // 判断数据是否加载完成，如果加载完成需要弹框，没有更多数据了
    if (!data.length) return showToast('没有更多数据了')
    // 判断是否是默认的聊天记录
    nextTick(() => {
      if (initChat.value) {
        console.log(document.body.scrollHeight, 'document.body.scrollHeight')

        socket.emit('updateMsgStatus', arr[arr.length - 1].id)
        window.scrollTo(0, document.body.scrollHeight)
        // 只有第一次获取聊天信息时才会滑动到底部
        initChat.value = false
      }
    })
  })
  // 订单状态发生改变时触发
  socket.on('statusChange', getOrderInfo)
  // 接收消息
  socket.on('receiveChatMsg', async (e: Message) => {
    // 将接收到的数据追加到消息列表中
    e.imageFlag = true
    chatMsgList.value.push(e)
    // 如果直接进行跳转，只是进行了数据修改，视图没有更新，应该等到视图更新之后在进行跳转
    await nextTick()
    socket.emit('updateMsgStatus', e.id) // 已读
    window.scrollTo(0, document.body.scrollHeight)
  })
})
onUnmounted(() => {
  // 关闭连接
  socket.close()
})
// 获取订单详情
// 1. 需要将将订单状态以及订单的倒计时传递到子组件中
// 2. 如果不是接诊状态，则底部应该处于禁用状态
const order = ref<ConsultOrderItem>()
const getOrderInfo = async () => {
  const { data } = await getOrderDetail(route.query.orderId as string)
  order.value = data
}
// 如果支付失败应该跳转到首页，根据路由上的payResult是否为true进行判断
onMounted(() => {
  if (route.query.payResult) {
    getOrderInfo()
  } else {
    console.log('支付失败')
  }
})
// 文字消息
// 1. 底部输入框发送数据，父组件接收底部组件传过来的方法，通过socket的sendChatMsg发送消息(emit)
// 2. 使用socket的receiveChatMsg接收消息(on)
// 3. 进行渲染
// 4. 当接收到医生传过来的消息时，页面需要滑动到最底端
// 5. 如果是获取默认的聊天记录，也需要跳转到底部
const initChat = ref(true)
const sendText = (text: string) => {
  socket.emit('sendChatMsg', {
    from: store.user?.id,
    to: order.value?.docInfo?.id,
    msgType: MsgType.MsgText,
    msg: {
      content: text
    }
  })
}
// 图片消息
// 1. 底部文件上传区域选择图片上传之后，父组件接收这个自定义事件，通过socket的sendChatMsg发送图片消息(emit)
// 2. 父组件通过socket.on('receiveChatMsg')接收消息，传递的数据和传递文字的数据中有两处内容不一样(类型和内容)
// 3. 进行渲染
// 上传图片的标志 imgFlag
const sendImage = (picture: Picture) => {
  socket.emit('sendChatMsg', {
    from: store.user?.id,
    to: order.value?.docInfo?.id,
    msgType: MsgType.MsgImage,
    msg: {
      picture
    }
  })
}
// 下拉刷新
// 1. 当松开手下拉刷新时发送请求，获取上一次的聊天记录，获取成功之后关闭loading，进行加载渲染
// 2. socket.emit('getChatMsgList',pageSize(30),lastTime<2023-02-02，最后一次时间>,orderId)
// 3. lastTime可以使用本次记录中的最早的时间
// 4. 请求完成之后需要手动关闭loading的状态，当没有再获取新的数据时则断定为没有更多历史记录了
// 5. 请求完成的数据列表需要追加到数据列表的前面
const loading = ref(false)
const lastTime = ref(dayjs().format('YYYY-MM-DD'))
const onRefresh = () => {
  // 发送获取聊天记录的事件
  socket.emit('getChatMsgList', 20, lastTime.value, route.query.orderId)
}
// 已读功能 -- 只针对MsgType为MsgText和MsgImage
// 1. 当接收数据后，当前数据包括之前的数据都设置为已读
// 2. 获取默认聊天记录时，设置最后一条聊天记录为已读
// 最新的消息id -~~~~根据最新的消息id,将之前的消息全部置为已读
// socket.emit('updateMsgStatus')

// 药品处方时，需要将当前路由上的参数保存到pinia中

// 评价内容
// 向评价组件传递医生ID和订单ID --- provide 和 inject
// 评价成功之后，需要在当前组件接收评价组件传递过来的自定义事件 ---- provide
provide('order', order)
const changeView = (score: number) => {
  // 查找数据列表中为评价类型的数据，在评价组件中只用到了score这个字段，因此只需要添加这个数据
  const item = chatMsgList.value.find(
    (item) => item.msgType === MsgType.CardEvaForm
  )
  if (item) {
    // 添加分数和修改类型
    item.msgType = MsgType.CardEva
    // 只用到了score这个属性，可以让evaluateDoc直接为包含分数的一个对象
    item.msg.evaluateDoc = { score }
  }
}
provide('changeView', changeView)
// 点击后退按钮，根据route中是否存在有payResult,区分从支付结果后回到问诊室还是支付成功后回到问诊时的情况
const back = () => {
  if (!route.query.type) {
    router.push({ name: 'ConsultRecord' })
  } else {
    router.push({ name: 'home' })
  }
}
</script>

<template>
  <div class="room-page">
    <DP-NavBar title="医生问诊室" :back="back"></DP-NavBar>
    <room-header
      :status="order?.status"
      :countdown="order?.countdown"
    ></room-header>
    <van-pull-refresh class="message" v-model="loading" @refresh="onRefresh">
      <room-message :initChat="initChat" :list="chatMsgList"></room-message>
    </van-pull-refresh>
    <room-footer
      class="footer"
      :disabled="order?.status !== OrderStatus.ConsultChat"
      @sendText="sendText"
      @sendImage="sendImage"
    ></room-footer>
  </div>
</template>

<style lang="scss" scoped>
.room-page {
  min-height: 100vh;
  overflow-y: auto;
  padding-top: 90px;
  padding-bottom: 60px;
  background-color: #f6f7f9;
  box-sizing: border-box;
}
</style>
